import {
  Component,
  OnInit,
  ViewEncapsulation,
  Input,
  ElementRef,
  Output,
  ContentChild,
  TemplateRef,
  EventEmitter,
  HostBinding,
} from '@angular/core';

import { fuiTranslateService } from '../i18n/i18n.module';

export class CalendarMonth {
  index: number;
  name: string;
  year: number;
  isCurrentMonth: boolean;
  isSelectedMonth: boolean;
  disabled: boolean;
}

export type CalendarQuartersType = Array<CalendarMonth>;

export class CalendarDay {
  number: number;
  isLastMonth: boolean;
  isNextMonth: boolean;
  isCurrentDay: boolean;
  isSelectedDay: boolean;
  title: string;
  date: Date;
  disabled: boolean;
  firstDisabled: boolean;
  lastDisabled: boolean;
}

export interface CalendarWeek {
  days: Array<CalendarDay>;
}

@Component({
  selector: 'fui-calendar-lite',
  templateUrl: './calendar-lite.component.html',
})
export class CalendarLiteComponent implements OnInit {
  weeksCalendar: Array<CalendarWeek> = [];
  quartersCalendar: Array<CalendarQuartersType> = [];
  listOfWeekName: Array<string> = [];
  listOfMonthName: Array<string> = [];
  listOfYearName: Array<string> = [];
  @ContentChild('dateCell') dateCell: TemplateRef<any>;
  @ContentChild('monthCell') monthCell: TemplateRef<any>;

  /** Click day event. */
  @Output() clickDayChange: EventEmitter<CalendarDay> = new EventEmitter();

  /** Click month event. */
  @Output() clickMonthChange: EventEmitter<CalendarMonth> = new EventEmitter();

  /** Clear date's time attributes. */
  @Input() clearTime = true;

  /** Display mode, support 'month' or 'year'. */
  @Input() mode: 'month' | 'year' = 'year';

  /** Define which date is disabled. */
  @Input() disabledDate: (date: Date) => boolean;

  private showMonthValue = (new Date()).getMonth();
  /** The month to show. */
  @Input()
  set showMonth(value: number) {
    this.showMonthValue = value;
    this.buildCalendar();
  }
  get showMonth(): number {
    return this.showMonthValue;
  }

  private showYearValue =  (new Date()).getFullYear();
  /** The year to show. */
  @Input()
  set showYear(value: number) {
    this.showYearValue = value;
    this.buildCalendar();
  }
  get showYear(): number {
    return this.showYearValue;
  }

  private valueSource: Date = new Date();
  /** A Single date that is selected. */
  @Input()
  set value(value: Date) {
    if (this.valueSource === value) {
      return;
    }
    this.valueSource = value;
    this.showMonth = this.valueSource && this.valueSource.getMonth();
    this.showYear = this.valueSource && this.valueSource.getFullYear();
  }
  get value(): Date {
    return this.valueSource || new Date();
  }

  private valueSources: Date[];
  /** An array of dates that are selected. For multi-select support. */
  @Input()
  set values(values: Date[]) {
    this.valueSources = values;
    this.buildCalendar();
  }
  get values(): Date[] {
    return this.valueSources;
  }

  constructor(
    private translate: fuiTranslateService,
  ) {}

  ngOnInit() {
    this.translate.onLangChange.subscribe(() => {
      this.buildCalendar();
    });
    this.buildCalendar();
  }

  removeTime(date: Date) {
    if (this.clearTime) {
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);
      return date;
    } else {
      return date;
    }
  }

  clickDay($event: MouseEvent, day: CalendarDay) {
    $event.preventDefault();
    $event.stopPropagation();
    if (day.disabled) {
      return;
    }
    this.clickDayChange.emit(day);
  }

  clickMonth($event: MouseEvent, month: CalendarMonth) {
    $event.preventDefault();
    $event.stopPropagation();
    if (month.disabled) {
      return;
    }
    this.clickMonthChange.emit(month);
  }

  buildMonth(d: Date): Array<CalendarWeek> {
    const weeks: Array<CalendarWeek> = [];
    const _rawDate = this.removeTime(d);
    const start = new Date(_rawDate);
    this.setMonday(start);
    const month = new Date(_rawDate);
    let done = false;
    const date = new Date(start);
    let monthIndex = date.getMonth();
    let count = 0;
    while (!done) {
      weeks.push({ days: this.buildWeek(new Date(date), month) });
      date.setDate(date.getDate() + 7);
      done = count++ > 4;
      monthIndex = date.getMonth();
    }
    return weeks;
  }

  buildWeek(date: Date, month: Date): Array<CalendarDay> {
    const days: Array<CalendarDay> = [];
    for (let i = 0; i < 7; i++) {
      const previousDate = new Date(date);
      previousDate.setDate(previousDate.getDate() - 1);
      const nextDate = new Date(date);
      nextDate.setDate(nextDate.getDate() + 1);

      days.push({
        number: date.getDate(),
        isLastMonth: date.getMonth() < month.getMonth(),
        isNextMonth: date.getMonth() > month.getMonth(),
        isCurrentDay: this.sameDay(date, new Date()),
        isSelectedDay: this.isSelectedDay(date),
        title: date.toLocaleDateString(this.translate.langBCP47),
        date,
        disabled: this.disabledDate && this.disabledDate(date),
        firstDisabled: this.disabledDate &&
          this.disabledDate(date) &&
          (date.getDay() === 0 || (date.getDay() !== 0 && this.disabledDate && !this.disabledDate(previousDate))),
        lastDisabled: this.disabledDate &&
          this.disabledDate(date) &&
          (date.getDay() === 6 || (date.getDay() !== 6 && this.disabledDate && !this.disabledDate(nextDate))),
      });
      date = new Date(date);
      date.setDate(date.getDate() + 1);
    }
    return days;
  }

  buildYears(date: Date) {
    const quarters = [];
    let months: Array<CalendarMonth> = [];
    for (let i = 0; i < 12; i++) {
      const currentMonth = new Date(date);
      currentMonth.setMonth(i);

      months.push({
        index: i,
        name: this.listOfMonthName[ i ],
        year: date.getFullYear(),
        isCurrentMonth: (new Date()).getMonth() === i && this.sameDay(date, new Date()),
        isSelectedMonth: this.showMonth === i,
        disabled: this.disabledDate && this.disabledDate(currentMonth),
      });
      if ((i + 1) % 3 === 0) {
        quarters.push(months);
        months = [];
      }
    }
    return quarters;
  }

  buildCalendar() {
    this.listOfYearName = this.generateYears(this.showYear);
    this.listOfWeekName = this.translate.translateKey('fui.DATEPICKER.WEEK_NAMES').split(',');
    this.listOfMonthName = this.translate.translateKey('fui.DATEPICKER.MONTH_NAMES').split(',');
    const date = new Date(this.value);
    date.setFullYear(this.showYear);
    date.setMonth(this.showMonth);
    this.weeksCalendar = this.buildMonth(date);
    this.quartersCalendar = this.buildYears(date);
  }

  generateYears(year: number) {
    const listOfYears = [];
    for (const i of Array.from(Array(20).keys())) {
      listOfYears.push(i - 10 + year);
    }
    return listOfYears;
  }

  sameDay(d1: Date, d2: Date) {
    return d1.getFullYear() === d2.getFullYear() &&
      d1.getMonth() === d2.getMonth() &&
      d1.getDate() === d2.getDate();
  }

  isSelectedDay(date: Date) {
    if (this.values) {
      return this.values.some((value) => this.sameDay(date, value));
    } else if (this.value) {
      return this.sameDay(date, this.value);
    }
    return false;
  }

  setMonday(date: Date) {
    date.setDate(1);
    const d = new Date(date);
    const day = d.getDay();
    const diff = d.getDate() - day + (day === 0 ? -6 : 1); // adjust when day is sunday
    return date.setDate(diff);
  }
}
